//////////////////////////////////////////////////////////////////////////////////
// Company:  
// Engineer:  
// Target Devices:  
// Tool versions:
//
// Create Date:    2011-08-18 15:28
// Project Name:    
// Description:
//
// Dependencies:
//
// Revision: 1.0
// Revision 0.01 - File Created
//
//
//////////////////////////////////////////////////////////////////////////////////
`timescale 1ns/1ns
module mcb_bfm
#(
    parameter ADDR_WIDTH  = 30,
    parameter DATA_WIDTH  = 32
)
(
    input                       clk,
    
    output                      ddr_cmd_clk,
    output reg                  ddr_cmd_en,
    output reg[2:0]             ddr_cmd_instr,
    output reg[5:0]             ddr_cmd_bl,
    output reg[ADDR_WIDTH-1:0]  ddr_cmd_byte_addr,
    input                       ddr_cmd_empty,
    input                       ddr_cmd_full,
    
    // write port
    output                      ddr_wr_clk,
    output reg                  ddr_wr_en,
    output reg[DATA_WIDTH/8-1:0]ddr_wr_mask,
    output reg[DATA_WIDTH-1:0]  ddr_wr_data,
    input 		                ddr_wr_full,
    input 		                ddr_wr_empty,
    input  [6:0]                ddr_wr_count,
    input                       ddr_wr_underrun,
    input                       ddr_wr_error,
   
    // read port
    output                      ddr_rd_clk,
    output reg                  ddr_rd_en,
    input [DATA_WIDTH-1:0]      ddr_rd_data,
    input		                ddr_rd_full,
    input		                ddr_rd_empty,
    input [6:0]	                ddr_rd_count,
    input                       ddr_rd_overflow,
    input                       ddr_rd_error
);

/********************************************************\
Parameter
\********************************************************/
localparam U_DLY = 1;
localparam WR = 3'b000;
localparam RD = 3'b001;

/********************************************************\
Signals
\********************************************************/

/********************************************************\
main code
\********************************************************/
assign ddr_cmd_clk = clk;
assign ddr_wr_clk  = clk;
assign ddr_rd_clk  = clk;

initial
begin
    ddr_cmd_en          = 1'b0;
    ddr_cmd_instr       = 'h0;
    ddr_cmd_bl          = 'h0;
    ddr_cmd_byte_addr   = 'h0;
    
    ddr_rd_en   = 1'b0;
    ddr_wr_mask = 'h0;
    ddr_wr_en   = 1'b0;
    ddr_wr_data = 'h0;
end

/*******************************************************\
	Write data from file to DDR
Useage:
	fp= fopen("tmp.dat","r");
	MCB_TASK_WR_FILE(fp,start_addr);
	fclose(fp);
\*******************************************************/	
task MCB_TASK_WR_FILE;
	input integer fp;
    input [ADDR_WIDTH-1:0] start_addr;
	integer state;
	integer	data_0,data_1,data_2,data_3;
	begin
		state = $fscanf(fp,"%h",data_0);
        state = $fscanf(fp,"%h",data_1);
        state = $fscanf(fp,"%h",data_2);
        state = $fscanf(fp,"%h",data_3);
        ddr_cmd_instr = WR;
        ddr_cmd_byte_addr = start_addr;
		while(state!=-1)
		begin
            wait(~ddr_wr_full);
            @(posedge ddr_wr_clk)
            begin
                ddr_wr_data <= #U_DLY (data_3<<24)|(data_2<<16)|(data_1<<8)|data_0;
                ddr_wr_en   <= #U_DLY 1'b1;
            end
            @(posedge ddr_wr_clk)
            begin
                ddr_wr_en   <= #U_DLY 1'b0;
            end
            wait(~ddr_cmd_full);
            @(posedge ddr_wr_clk)
            begin
                ddr_cmd_en  <= #U_DLY 1'b1;
                ddr_cmd_byte_addr <= #U_DLY ddr_cmd_byte_addr;
            end
            @(posedge ddr_wr_clk)
            begin
                ddr_cmd_en  <= #U_DLY 1'b0;
                ddr_cmd_byte_addr   <= #U_DLY ddr_cmd_byte_addr + 4;
            end
            state = $fscanf(fp,"%h",data_0);
            state = $fscanf(fp,"%h",data_1);
            state = $fscanf(fp,"%h",data_2);
            state = $fscanf(fp,"%h",data_3);
		end
	end
endtask

/*******************************************************\
	Read data from ddr
Useage:
	fp= fopen("tmp.dat","w");
	MCB_TASK_RD2FILE(fp,start_addr,len);
	fclose(fp);
\*******************************************************/	
task MCB_TASK_RD2FILE;
	input integer fp;
    input [ADDR_WIDTH-1:0] start_addr;
    input integer len;
    integer cnt;
	begin
        cnt = 0;
        ddr_cmd_instr = RD;
        ddr_cmd_byte_addr = start_addr;
        while(cnt<len)
        begin
            wait(ddr_cmd_full==1'b0);
            @(posedge ddr_cmd_clk)
            begin
                ddr_cmd_en <= #U_DLY 1'b1;
            end
            @(posedge ddr_cmd_clk)
            begin
                ddr_cmd_en <= #U_DLY 1'b0;
            end
            wait(ddr_rd_empty==1'b0);
            @(posedge ddr_rd_clk)
            begin
                ddr_rd_en <= #U_DLY 1'b1;
                $fdisplay(fp,"%h",ddr_rd_data[7:0]);
                $fdisplay(fp,"%h",ddr_rd_data[15:8]);
                $fdisplay(fp,"%h",ddr_rd_data[23:16]);
                $fdisplay(fp,"%h",ddr_rd_data[31:24]);
            end
            @(posedge ddr_rd_clk)
            begin
                ddr_rd_en <= #U_DLY 1'b0;
            end
            ddr_cmd_byte_addr = ddr_cmd_byte_addr + 4;
            cnt = cnt + 4;
        end
	end
endtask

endmodule
